////////////////////////////////////////////////////////////////////////////////////////////////////
//
//	Crytek Character Animation source code
//	
//	History:
//	28/09/2004 - Created by Ivo Herzeg <ivo@crytek.de>
//
//  Contains:
//  Implementation of all software skinning & morphing functions
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include <IRenderAuxGeom.h>
#include "Model.h"
#include "ModelMesh.h"
#include "ModelSkeleton.h"
#include "CharacterInstance.h"
#include "CharacterManager.h"
#include "CryModEffMorph.h"
#include "GeomQuery.h"

static DynArray<Vec3> g_arrExtSkinnedStream;
static DynArray<Vec3> g_arrExtMorphStream;			//the second stream to add the morph-targets to the ExtSkinVertices
static DynArray<SPipTangents> g_arrTangents;


void BlendQuatDs(QuatD& qd, QuatD const aSkinQuats[], BoneID indices, ColorB weights)
{
	// Get 8-bit weights as floats
	f32 w0 = weights[0];
	f32 w1 = weights[1];
	f32 w2 = weights[2];
	f32 w3 = weights[3];

	QuatD const& q0 = aSkinQuats[indices.idx0];
	QuatD const& q1 = aSkinQuats[indices.idx1];
	QuatD const& q2 = aSkinQuats[indices.idx2];
	QuatD const& q3 = aSkinQuats[indices.idx3];

	// Blend unnormalised
	qd = q0*w0 + q1*w1 + q2*w2 + q3*w3;
	qd.Normalize();
}


CloseInfo CModelMesh::FindClosestPointOnMesh( const Vec3& RMWPosition, bool bUseCenterPointDist ) 
{ 

	f32 distance = 99999999.0f;
	uint32 face = ~0;
	Vec3 ClosestPoint;

	//SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	//renderFlags.SetFillMode( e_FillModeWireframe );
	//renderFlags.SetDrawInFrontMode( e_DrawInFrontOn );
	//g_pAuxGeom->SetRenderFlags( renderFlags );

	LockFullRenderMesh(m_pModel->m_nBaseLOD);
	CloseInfo cf;
	uint32 foundPos(~0);
	for(int d=0; d<m_Indices; d += 3)	
	{
		Vec3 v0 = (*(Vec3*)(m_pPositions + m_pIndices[d+0] * m_PosStride));//m_arrExtVertices[ m_arrExtFaces[d].i0 ].wpos1;
		Vec3 v1 = (*(Vec3*)(m_pPositions + m_pIndices[d+1] * m_PosStride));//m_arrExtVertices[ m_arrExtFaces[d].i1 ].wpos1;
		Vec3 v2 = (*(Vec3*)(m_pPositions + m_pIndices[d+2] * m_PosStride));//m_arrExtVertices[ m_arrExtFaces[d].i2 ].wpos1;

		//g_pAuxGeom->DrawLine( v0,RGBA8(0xff,0x00,0x00,0x00), v1,RGBA8(0x00,0xff,0x00,0x00) );
		//g_pAuxGeom->DrawLine( v1,RGBA8(0x00,0xff,0x00,0x00), v2,RGBA8(0x00,0x00,0xff,0x00) );
		//g_pAuxGeom->DrawLine( v2,RGBA8(0x00,0x00,0xff,0x00), v0,RGBA8(0xff,0x00,0x00,0x00) );

		Vec3 TriMiddle=(v0+v1+v2)/3.0f;

		f32 sqdist=0;
		if( bUseCenterPointDist )
			sqdist=(TriMiddle-RMWPosition)|(TriMiddle-RMWPosition);
		else
			sqdist=Distance::Point_TriangleSq(RMWPosition,Triangle(v0,v1,v2));
		//
		if 	(distance>sqdist) 
		{
			cf.tv0		= v0;
			cf.tv1		= v1;
			cf.tv2		= v2;

			cf.middle	= TriMiddle;
			distance=sqdist;
			foundPos = d;
			ClosestPoint=TriMiddle;
		}
	}

	for (uint32 i = 0, end = m_arrExtVerticesCached.size(); i < end; i += 3)
	{
		Vec3 v0 = m_arrExtVerticesCached[i+0].wpos1;
		Vec3 v1 = m_arrExtVerticesCached[i+1].wpos1;
		Vec3 v2 = m_arrExtVerticesCached[i+2].wpos1;

		//	g_pAuxGeom->DrawLine( v0,RGBA8(0xff,0x00,0x00,0x00), v1,RGBA8(0x00,0xff,0x00,0x00) );
		//	g_pAuxGeom->DrawLine( v1,RGBA8(0x00,0xff,0x00,0x00), v2,RGBA8(0x00,0x00,0xff,0x00) );
		//	g_pAuxGeom->DrawLine( v2,RGBA8(0x00,0x00,0xff,0x00), v0,RGBA8(0xff,0x00,0x00,0x00) );

		if (m_arrExtVerticesCached[i].wpos1 == cf.tv0 && m_arrExtVerticesCached[i+1].wpos1 == cf.tv1 && m_arrExtVerticesCached[i+2].wpos1 == cf.tv2)
		{
			face = i/3;
			break;
		}
	}


	if ( face == ~0 )
	{
		for ( uint32 i = 0; i < 3; ++i )
		{
			uint32 fp = foundPos + i;
			uint32 e = m_pIndices[ fp ];
			ExtSkinVertex skin = GetSkinVertexNoInd( e );

			AttSkinVertex vertex;
			vertex.FromExtSkinVertex( skin );
			vertex.org_vertex_index = e;
			m_arrExtVerticesCached.push_back( vertex );
		}

		face = ( m_arrExtVerticesCached.size() - 1 ) / 3;
	}

	UnlockFullRenderMesh(m_pModel->m_nBaseLOD);
	cf.FaceNr	=	face;

	return cf;
}


volatile int meshGuard;

void CModelMesh::LockFullRenderMesh(int lod)
{
	WriteLock lock(meshGuard);

	++m_iThreadMeshAccessCounter;

	IRenderMesh * pMesh = m_pModel->GetRenderMesh(/*m_pModel->m_nBaseLOD*//*m_nLOD*/lod);
	pMesh->LockForThreadAccess();

	m_Indices = pMesh->GetIndicesCount();
	m_pPositions = pMesh->GetPosPtr(m_PosStride, FSL_READ);
	m_pIndices = pMesh->GetIndexPtr(FSL_READ);
#if !defined(PS3)
	m_pSkinningInfo = pMesh->GetHWSkinPtr(m_SkinStride, FSL_READ);
	#if !defined(SHAPE_DEFORMATION_DISABLED)
		m_pMorphingInfo = pMesh->GetShapePtr(m_ShapeStride, FSL_READ);
	#endif
	m_pUVs = pMesh->GetUVPtr(m_UVStride, FSL_READ);
	m_pColors = pMesh->GetColorPtr(m_ColorStride, FSL_READ);
	m_pTangents = pMesh->GetTangentPtr(m_TangentStride, FSL_READ);
	m_pBinormals = pMesh->GetBinormalPtr(m_BinormalStride, FSL_READ);
#endif
}

void CModelMesh::UnlockFullRenderMesh(int lod)
{
	WriteLock lock(meshGuard);

	--m_iThreadMeshAccessCounter;
	if (m_iThreadMeshAccessCounter == 0) {
		IRenderMesh * pMesh = m_pModel->GetRenderMesh(lod/*m_nLOD*/);
		pMesh->UnlockStream(VSF_GENERAL);
#if !defined(XENON) && !defined(PS3) 
    pMesh->UnlockStream(VSF_HWSKIN_SHAPEDEFORM_INFO);
#endif
		pMesh->UnlockStream(VSF_HWSKIN_INFO);
		pMesh->UnlockStream(VSF_TANGENTS);
		pMesh->UnLockForThreadAccess();
	}
}

ExtSkinVertex CModelMesh::GetSkinVertexNoInd(int pos)
{
	ExtSkinVertex vertex;

	vertex.wpos1 = (*(Vec3*)(m_pPositions + pos * m_PosStride));














	if (m_pBinormals)
		vertex.binormal = *(Vec4sf*)(m_pBinormals + pos * m_BinormalStride);
	if (m_pTangents)
		vertex.tangent = *(Vec4sf*)(m_pTangents + pos * m_TangentStride);

	if (!m_arrExtBoneIDs.empty())
		vertex.boneIDs = *(ColorB*)&m_arrExtBoneIDs[pos];
	else
		vertex.boneIDs = *(ColorB*)&((SVF_W4B_I4B*)(m_pSkinningInfo + pos * m_SkinStride))->indices;
	vertex.weights = *(ColorB*)&((SVF_W4B_I4B*)(m_pSkinningInfo + pos * m_SkinStride))->weights;

	//vertex.color = *((ColorB*)(m_pColors + pos * m_ColorStride));
  Vec2f16 stf16 = *(Vec2f16*)(m_pUVs + pos * m_UVStride);
  Vec2 st = stf16.ToVec2();
	vertex.u = st.x;
	vertex.v = st.y;

	if (m_pMorphingInfo)
	{
		vertex.wpos0 = ((SVF_P3F_P3F_I4B*)(m_pMorphingInfo + pos * m_ShapeStride))->thin;
		vertex.wpos2 = ((SVF_P3F_P3F_I4B*)(m_pMorphingInfo + pos * m_ShapeStride))->fat;
		vertex.color = *(ColorB*)&((SVF_P3F_P3F_I4B*)(m_pMorphingInfo + pos * m_ShapeStride))->index;
	}
	else
	{
		vertex.wpos0 = vertex.wpos1;
		vertex.wpos2 = vertex.wpos1;
		vertex.color.r=0;
		vertex.color.g=0;
		vertex.color.b=0;
		vertex.color.a=0;
	}

#if !(defined(XENON) || defined(PS3))
	assert(vertex.color.a<8);
	std::swap(vertex.color.r, vertex.color.b);
#endif

	return vertex;

}

void  AttSkinVertex::FromExtSkinVertex(const ExtSkinVertex& skin)
{
	wpos0=skin.wpos0;
	wpos1=skin.wpos1;
	wpos2=skin.wpos2;
	color=skin.color;
	weights[0]=skin.weights[0]/255.0f;
	weights[1]=skin.weights[1]/255.0f;
	weights[2]=skin.weights[2]/255.0f;
	weights[3]=skin.weights[3]/255.0f;
	boneIDs[0]=skin.boneIDs[0];
	boneIDs[1]=skin.boneIDs[1];
	boneIDs[2]=skin.boneIDs[2];
	boneIDs[3]=skin.boneIDs[3];
}


//////////////////////////////////////////////////////////////////////////
// skinning of internal vertices 
//////////////////////////////////////////////////////////////////////////
void CModelMesh::InitSkinningExtSW ( const CMorphing* pMorphing, const f32* pShapeDeform, int nLOD)
{
	//-----------------------------------------------------------------------
	//----           Create the internal morph-target stream             ----
	//-----------------------------------------------------------------------
	if (nLOD==0 && pMorphing && pMorphing->NeedMorph() && Console::GetInst().ca_UseMorph)
	{
		//initialize morph-target stream
		int32 numExtVertices = m_arrExtBoneIDs.size();
		if (g_arrExtMorphStream.size() < numExtVertices)
			g_arrExtMorphStream.resize(numExtVertices);
		memset(g_arrExtMorphStream.begin(),0,numExtVertices*sizeof(Vec3));

		uint32 numMorphEffectors = pMorphing->m_arrMorphEffectors.size();

		Vec3 vUnpackedVertex; 
		for (uint32 m=0; m<numMorphEffectors; ++m)
		{
			const CryModEffMorph& rMorphEffector = pMorphing->m_arrMorphEffectors[m];
			int nMorphTargetId = rMorphEffector.getMorphTargetId ();
			if (nMorphTargetId < 0)
				continue;
			CMorphTarget* pMorphSkin = this->m_pModel->getMorphSkin (0, nMorphTargetId);

			float fBalance = rMorphEffector.m_Params.m_fBalance;
			float fBalanceL = 0;
			float fBalanceR = 0;
			if (fBalance < 0)
				fBalanceL = fabs(fBalance);
			else
				fBalanceR = fabs(fBalance);
			float fBalanceBase = 1.0f - fabs(fBalance);

			float fBlending = rMorphEffector.getBlending();
			float _fBase = fBlending*fBalanceBase;
			float _fBalL = fBlending*fBalanceL;
			float _fBalR = fBlending*fBalanceR;

			uint32 numSkinVerts = pMorphSkin->m_vertices.size();
			f32 ex = pMorphSkin->m_MTExtensions.x/255.0f;
			f32 ey = pMorphSkin->m_MTExtensions.y/255.0f;
			f32 ez = pMorphSkin->m_MTExtensions.z/255.0f;
			for(uint32 i=0; i<numSkinVerts; i++) 
			{
				// add all morph-values to the stream to use subsequently in skinning
				f32 l=f32(pMorphSkin->m_vertices[i].m_fBalanceLR&0x0f)/15.0f;
				f32 r=f32(pMorphSkin->m_vertices[i].m_fBalanceLR>>0x4)/15.0f; 
				float fVertexBlend = _fBase +  _fBalL*l +  _fBalR*r;

				uint32 idx = pMorphSkin->m_vertices[i].m_nVertexId;
				f32 x=f32(pMorphSkin->m_vertices[i].m_MTVertexX);
				f32 y=f32(pMorphSkin->m_vertices[i].m_MTVertexY);
				f32 z=f32(pMorphSkin->m_vertices[i].m_MTVertexZ);
				vUnpackedVertex.x = x*ex+pMorphSkin->m_MTNegBasis.x;
				vUnpackedVertex.y = y*ey+pMorphSkin->m_MTNegBasis.y;
				vUnpackedVertex.z = z*ez+pMorphSkin->m_MTNegBasis.z;
				g_arrExtMorphStream[idx] += vUnpackedVertex * fVertexBlend;
			}
		}
	}

	//-----------------------------------------------------------------------
	//----    Create 8 Vec4 vectors that contain the blending values     ----
	//---                    to blend between 3 models                    ---
	//-----------------------------------------------------------------------
	for (uint32 i=0; i<8; i++) 
	{
		if (!pShapeDeform)
		{
			VertexRegs[i].x	=	0;
			VertexRegs[i].y	=	1;
			VertexRegs[i].z	=	0;
		}
		else if (pShapeDeform[i] < 0.0f)
		{
			VertexRegs[i].x	=	1-(pShapeDeform[i]+1);
			VertexRegs[i].y	=	pShapeDeform[i]+1;
			VertexRegs[i].z	=	0;
		} 
		else 
		{
			VertexRegs[i].x	=	0;
			VertexRegs[i].y	=	1-pShapeDeform[i];
			VertexRegs[i].z	=	pShapeDeform[i];
		}
	}
}


//////////////////////////////////////////////////////////////////////////
// skinning of external internal vertices and tangents 
//////////////////////////////////////////////////////////////////////////
void CModelMesh::SkinningExtSW ( CCharInstance* pMaster, CAttachment* pAttachment, int nLOD, int nList)
{

#ifdef DEFINE_PROFILER_FUNCTION
	DEFINE_PROFILER_FUNCTION();
#endif
	InitSkinningExtSW(&pMaster->m_Morphing,pMaster->m_arrShapeDeformValues,nLOD);

	QuatTS* pMasterSkinningTransformations = pMaster->GetSkinningTransformations(nList);
	QuatTS arrNewSkinQuat[MAX_JOINT_AMOUNT+1];	//bone quaternions for skinning (entire skeleton)
//	uint32 iActiveFrame		=	pMaster->m_iActiveFrame;
	if(pAttachment)
	{
		uint32 numRemapJoints	= pAttachment->m_arrRemapTable.size();
		for (uint32 i=0; i<numRemapJoints; i++)
			arrNewSkinQuat[i] = pMasterSkinningTransformations[pAttachment->m_arrRemapTable[i]];
	}
	else
	{
		uint32 numJoints	= pMaster->m_SkeletonPose.GetJointCount();
		cryMemcpy(arrNewSkinQuat, pMasterSkinningTransformations, sizeof(QuatTS)*numJoints);
	}

	int32 numExtVertices = m_pModel->GetRenderMesh(nLOD)->GetVerticesCount();//m_arrTransformCache.size();	assert(numExtVertices);
	if (g_arrExtSkinnedStream.size() < numExtVertices)
		g_arrExtSkinnedStream.resize(numExtVertices);

	LockFullRenderMesh(nLOD);

	if (g_arrTangents.size()<numExtVertices)
		g_arrTangents.resize(numExtVertices);

	for (int32 i=0; i<numExtVertices; ++i)
		VertexShader_SBS(arrNewSkinQuat, i);

	UnlockFullRenderMesh(nLOD);
}




//---------------------------------------------------------------------
//----          this part of code belongs into hardware            ----
//---    this is a simulation of the spherical-vertex-blending      ---
//---------------------------------------------------------------------
void CModelMesh::VertexShader_SBS(QuatTS* parrNewSkinQuat,uint32 i) 
{
	ExtSkinVertex vertex = GetSkinVertexNoInd(i);
	//create the final vertex for skinning (blend between 3 characters)



	uint8 idx		=	vertex.color.a;  


	Vec3 v	= vertex.wpos0*VertexRegs[idx].x + vertex.wpos1*VertexRegs[idx].y + vertex.wpos2*VertexRegs[idx].z;
	//apply morph-targets
	if (g_arrExtMorphStream.size())
		v+=g_arrExtMorphStream[i];

	QuatD qd;
	BlendQuatDs(qd, (const QuatD*)parrNewSkinQuat, m_arrExtBoneIDs[i], vertex.weights);

	g_arrExtSkinnedStream[i] = qd * v;

	Vec3 binormal	= Vec3( tPackB2F(vertex.binormal.x),tPackB2F(vertex.binormal.y),tPackB2F(vertex.binormal.z) );
	Vec3 b = qd.nq * binormal;
	g_arrTangents[i].Binormal = Vec4sf(tPackF2B(b.x),tPackF2B(b.y),tPackF2B(b.z), vertex.binormal.w);

	Vec3 tangent	= Vec3( tPackB2F(vertex.tangent.x), tPackB2F(vertex.tangent.y), tPackB2F(vertex.tangent.z) );
	Vec3 t = qd.nq * tangent;
	g_arrTangents[i].Tangent	= Vec4sf(tPackF2B(t.x),tPackF2B(t.y),tPackF2B(t.z), vertex.tangent.w);
}; 




//---------------------------------------------------------------------
//----            this part of code belongs into hardware          ----
//---   this is a simulation of the smooth-skinning vertex-shader  ---
//---------------------------------------------------------------------
Vec3 CModelMesh::GetSkinnedExtVertex2( QuatTS* parrNewSkinQuat, uint32 i) 
{
	//--------------------------------------------------------
	//----   this part of code belongs into hardware      ----
	//---  this loop is a simulation of the vertex-shader  ---
	//--------------------------------------------------------
	//create the final vertex for skinning (blend between 3 characters)
	ExtSkinVertex vertex = GetSkinVertexNoInd(i);



	uint8 idx		=	vertex.color.a; 

	Vec3 v	= vertex.wpos0*VertexRegs[idx].x + vertex.wpos1*VertexRegs[idx].y + vertex.wpos2*VertexRegs[idx].z;
	//apply morph-targets
	if (g_arrExtMorphStream.size())
		v+=g_arrExtMorphStream[i];

	QuatD qd;
	BlendQuatDs(qd, (const QuatD*)parrNewSkinQuat, m_arrExtBoneIDs[i], vertex.weights);
	return qd * v;
}

void CModelMesh::DrawWireframeStatic( CMorphing* pMorphing, const Matrix34& m34, int nLOD, uint32 color)
{
	if (nLOD < m_pModel->m_nBaseLOD)
		nLOD = m_pModel->m_nBaseLOD;

	f32 arrShapeDeformValues[8] = {0,0,0,0, 0,0,0,0};
	InitSkinningExtSW(NULL,arrShapeDeformValues,nLOD);

	uint32 numExtIndices	= m_pModel->GetRenderMesh(nLOD)->GetIndicesCount();
	uint32 numExtVertices	=	m_pModel->GetRenderMesh(nLOD)->GetVerticesCount();
	assert(numExtVertices);

	static std::vector<ColorB> arrExtVColors;
	uint32 csize = arrExtVColors.size();
	if (csize<numExtVertices) arrExtVColors.resize( numExtVertices );	

	static std::vector<Vec3> arrExtSkinnedStream;
	uint32 vsize = arrExtSkinnedStream.size();
	if (vsize<numExtVertices) arrExtSkinnedStream.resize( numExtVertices );	
	LockFullRenderMesh(nLOD); 
	for(uint32 e=0; e<numExtVertices; e++) 
	{	
		//create the final vertex for skinning (blend between 3 characters)
		ExtSkinVertex vertex = GetSkinVertexNoInd(e);
		arrExtVColors[e]=vertex.color;
		uint8 idx	=	vertex.color.a; 
		Vec3 v		= vertex.wpos0*VertexRegs[idx].x + vertex.wpos1*VertexRegs[idx].y + vertex.wpos2*VertexRegs[idx].z;
		if (g_arrExtMorphStream.size())
			v+=g_arrExtMorphStream[e];
		arrExtSkinnedStream[e] = m34*v;
	}
	UnlockFullRenderMesh(nLOD);

	SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	renderFlags.SetFillMode( e_FillModeWireframe );
	renderFlags.SetDrawInFrontMode( e_DrawInFrontOn );
	renderFlags.SetAlphaBlendMode(e_AlphaAdditive);
	g_pAuxGeom->SetRenderFlags( renderFlags );
	g_pAuxGeom->DrawTriangles(&arrExtSkinnedStream[0],numExtVertices, m_pIndices,numExtIndices,color);		
}



void CModelMesh::DrawWireframe ( CMorphing* pMorphing, QuatTS* parrNewSkinQuat, const f32* pShapeDeform, int nLOD, const Matrix34& rRenderMat34 )
{
	if(m_arrExtBoneIDs.size()==0)
	{
		return;
	}

	InitSkinningExtSW(pMorphing,pShapeDeform,nLOD);

	uint32 numExtIndices	= m_pModel->GetRenderMesh(nLOD)->GetIndicesCount();
	uint32 numExtVertices	=	m_pModel->GetRenderMesh(nLOD)->GetVerticesCount();
	assert(numExtVertices);

	static std::vector<ColorB> arrExtVColors;
	uint32 csize = arrExtVColors.size();
	if (csize<numExtVertices) arrExtVColors.resize( numExtVertices );	


	static std::vector<Vec3> arrExtSkinnedStream;
	uint32 vsize = arrExtSkinnedStream.size();
	if (vsize<numExtVertices) arrExtSkinnedStream.resize( numExtVertices );	


	LockFullRenderMesh(nLOD);

	for(uint32 e=0; e<numExtVertices; e++) 
	{	
		//create the final vertex for skinning (blend between 3 characters)

		ExtSkinVertex vertex = GetSkinVertexNoInd(e);
		arrExtVColors[e]=vertex.color;
		uint8 idx	=	vertex.color.a; 
		assert(idx<8); 
		Vec3 v		= vertex.wpos0*VertexRegs[idx].x + vertex.wpos1*VertexRegs[idx].y + vertex.wpos2*VertexRegs[idx].z;
		//apply morph-targets
		if (!g_arrExtMorphStream.empty())
			v+=g_arrExtMorphStream[e];

		QuatD qd;
		BlendQuatDs(qd, (const QuatD*)parrNewSkinQuat, m_arrExtBoneIDs[e], vertex.weights);
		Vec3 sv = qd * v;
		arrExtSkinnedStream[e] = rRenderMat34*sv;
	}

	UnlockFullRenderMesh(nLOD);
	SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	renderFlags.SetFillMode( e_FillModeWireframe );
	renderFlags.SetDrawInFrontMode( e_DrawInFrontOn );
	g_pAuxGeom->SetRenderFlags( renderFlags );
	g_pAuxGeom->DrawTriangles(&arrExtSkinnedStream[0],numExtVertices, m_pIndices,numExtIndices,&arrExtVColors[0]);		
}


#define VLENGTH (0.05f)
//--------------------------------------------------------------------
//---             draw the Tangents of a character                  ---
//--------------------------------------------------------------------
void CModelMesh::DrawTangents ( const Matrix34& rRenderMat34)
{

	//	uint32 numExtIndices	= m_pModel->GetRenderMesh(m_pModel->m_nBaseLOD)->GetSysIndicesCount();
	uint32 numExtVertices	=	g_arrExtSkinnedStream.size();
	assert(numExtVertices);

	static std::vector<ColorB> arrExtVColors;
	uint32 csize = arrExtVColors.size();
	if (csize<(numExtVertices*2)) arrExtVColors.resize( numExtVertices*2 );	
	for(uint32 i=0; i<numExtVertices*2; i=i+2)	
	{
		arrExtVColors[i+0] = RGBA8(0x3f,0x00,0x00,0x00);
		arrExtVColors[i+1] = RGBA8(0xff,0x7f,0x7f,0x00);
	}

	Matrix34 WMat34 = rRenderMat34;
	Matrix33 WMat33 = Matrix33(rRenderMat34);
	static std::vector<Vec3> arrExtSkinnedStream;
	uint32 vsize = arrExtSkinnedStream.size();
	if (vsize<(numExtVertices*2)) arrExtSkinnedStream.resize( numExtVertices*2 );	
	for(uint32 i=0,t=0; i<numExtVertices; i++)	
	{
		Vec3 tangent  = Vec3( tPackB2F(g_arrTangents[i].Tangent.x), tPackB2F(g_arrTangents[i].Tangent.y), tPackB2F(g_arrTangents[i].Tangent.z) )*VLENGTH;
		arrExtSkinnedStream[t+0] = WMat34*g_arrExtSkinnedStream[i]; 
		arrExtSkinnedStream[t+1] = WMat33*tangent+arrExtSkinnedStream[t];
		t=t+2;
	}

	SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	g_pAuxGeom->SetRenderFlags( renderFlags );
	g_pAuxGeom->DrawLines( &arrExtSkinnedStream[0],numExtVertices*2, &arrExtVColors[0]);		
}


//--------------------------------------------------------------------
//---             draw the Binormals of a character                ---
//--------------------------------------------------------------------
void CModelMesh::DrawBinormals( const Matrix34& rRenderMat34)
{
	//uint32 numExtIndices	= m_pModel->GetRenderMesh(m_pModel->m_nBaseLOD)->GetSysIndicesCount();
	uint32 numExtVertices	=	g_arrExtSkinnedStream.size();
	assert(numExtVertices);

	static std::vector<ColorB> arrExtVColors;
	uint32 csize = arrExtVColors.size();
	if (csize<(numExtVertices*2)) arrExtVColors.resize( numExtVertices*2 );	
	for(uint32 i=0; i<numExtVertices*2; i=i+2)	
	{
		arrExtVColors[i+0] = RGBA8(0x00,0x3f,0x00,0x00);
		arrExtVColors[i+1] = RGBA8(0x7f,0xff,0x7f,0x00);
	}

	Matrix34 WMat34 = rRenderMat34;
	Matrix33 WMat33 = Matrix33(rRenderMat34);
	static std::vector<Vec3> arrExtSkinnedStream;
	uint32 vsize = arrExtSkinnedStream.size();
	if (vsize<(numExtVertices*2)) arrExtSkinnedStream.resize( numExtVertices*2 );	
	for(uint32 i=0,t=0; i<numExtVertices; i++)	
	{
		Vec3 binormal = Vec3( tPackB2F(g_arrTangents[i].Binormal.x), tPackB2F(g_arrTangents[i].Binormal.y), tPackB2F(g_arrTangents[i].Binormal.z) )*VLENGTH;
		arrExtSkinnedStream[t+0] = WMat34*g_arrExtSkinnedStream[i]; 
		arrExtSkinnedStream[t+1] = WMat33*binormal+arrExtSkinnedStream[t];
		t=t+2;
	}

	SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	g_pAuxGeom->SetRenderFlags( renderFlags );
	g_pAuxGeom->DrawLines( &arrExtSkinnedStream[0],numExtVertices*2, &arrExtVColors[0]);		
}

//--------------------------------------------------------------------
//---             draw the normals of a character                  ---
//--------------------------------------------------------------------
void CModelMesh::DrawNormals ( const Matrix34& rRenderMat34)
{
	uint32 numExtVertices	=	g_arrExtSkinnedStream.size();
	assert(numExtVertices);

	static std::vector<ColorB> arrExtVColors;
	uint32 csize = arrExtVColors.size();
	if (csize<(numExtVertices*2)) arrExtVColors.resize( numExtVertices*2 );	
	for(uint32 i=0; i<numExtVertices*2; i=i+2)	
	{
		arrExtVColors[i+0] = RGBA8(0x00,0x00,0x3f,0x00);
		arrExtVColors[i+1] = RGBA8(0x7f,0x7f,0xff,0x00);
	}

	Matrix34 WMat34 = rRenderMat34;
	Matrix33 WMat33 = Matrix33(rRenderMat34);
	static std::vector<Vec3> arrExtSkinnedStream;
	uint32 vsize = arrExtSkinnedStream.size();
	if (vsize<(numExtVertices*2)) arrExtSkinnedStream.resize( numExtVertices*2 );	
	for(uint32 i=0,t=0; i<numExtVertices; i++)	
	{
		f32  flip	=     tPackB2F(g_arrTangents[i].Binormal.w)*VLENGTH;
		Vec3 binormal	= Vec3( tPackB2F(g_arrTangents[i].Binormal.x),tPackB2F(g_arrTangents[i].Binormal.y),tPackB2F(g_arrTangents[i].Binormal.z) );
		Vec3 tangent  = Vec3( tPackB2F(g_arrTangents[i].Tangent.x), tPackB2F(g_arrTangents[i].Tangent.y), tPackB2F(g_arrTangents[i].Tangent.z) );
		Vec3 normal = (tangent%binormal)*flip;
		arrExtSkinnedStream[t+0] = WMat34*g_arrExtSkinnedStream[i]; 
		arrExtSkinnedStream[t+1] = WMat33*normal+arrExtSkinnedStream[t];
		t=t+2;
	}

	SAuxGeomRenderFlags renderFlags( e_Def3DPublicRenderflags );
	g_pAuxGeom->SetRenderFlags( renderFlags );
	g_pAuxGeom->DrawLines( &arrExtSkinnedStream[0],numExtVertices*2, &arrExtVColors[0]);		
}



uint32 CModelMesh::GetVertextCount() const 
{
	if (m_pModel && m_pModel->m_pRenderMeshs[m_pModel->m_nBaseLOD])
	{
		IRenderMesh *pMesh = m_pModel->GetRenderMesh(m_pModel->m_nBaseLOD/*m_nLOD*/);
		return pMesh->GetVerticesCount();
	} 

	return 0;
}


void CModelMesh::DrawDebugInfo(CCharInstance* pInstance, int nLOD,const Matrix34& rRenderMat34, int DebugMode, IMaterial* & pMaterial,  CRenderObject *pObj, const SRendParams& RendParams)
{
	bool bNoText = DebugMode < 0;

	int32 numLODs = m_pModel->m_arrModelMeshes.size();
	int index = 0;
	Vec3 trans = rRenderMat34.GetTranslation();
	trans += (m_pModel->m_arrCollisions[index].m_aABB.max - m_pModel->m_arrCollisions[index].m_aABB.min)/2.0f + m_pModel->m_arrCollisions[index].m_aABB.min;
	float color[4] = {1,1,1,1};

	IRenderMesh * pRenderMesh = m_pModel->GetRenderMesh(nLOD);
	int nTris = pRenderMesh ? pRenderMesh->GetVerticesCount() : 0;
	int nMats = pRenderMesh ? pRenderMesh->GetChunks().Count() : 0;
	int nRenderMats=0;

	string shortName = PathUtil::GetFile(m_pModel->m_strFilePath);

	IRenderAuxGeom *pAuxGeom = g_pIRenderer->GetIRenderAuxGeom();

	if(nMats)
	{
		for(int i=0;i<nMats;++i)
		{	
			CRenderChunk &rc = pRenderMesh->GetChunks()[i];
			if(rc.pRE && rc.nNumIndices && rc.nNumVerts && ((rc.m_nMatFlags&MTL_FLAG_NODRAW)==0))
				++nRenderMats;
		}
	}

	switch (DebugMode)
	{
	case 1:
		if (numLODs > 1)
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%s\n%d (LOD %d/%d)",shortName.c_str(),nTris,nLOD+1,numLODs );
		else
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%s\n%d",shortName.c_str(),nTris);

			pAuxGeom->DrawAABB( m_pModel->m_ModelAABB,rRenderMat34,false,ColorB(0,255,255,128),eBBD_Faceted );
		break;
	case 2:
		{

			IMaterialManager* pMatMan = g_pI3DEngine->GetMaterialManager();
			int fMult = 1;
			//int nTris = m_pModel->GetRenderMesh(nLOD)->GetSysVertCount();
			ColorB clr = ColorB(255,255,255,255);
			if (nTris >= 20000*fMult)
				clr = ColorB(255,0,0,255);
			else if (nTris >= 10000*fMult)
				clr = ColorB(255,255,0,255);
			else if (nTris >= 5000*fMult)
				clr = ColorB(0,255,0,255);
			else if (nTris >= 2500*fMult)
				clr = ColorB(0,255,255,255);
			else if (nTris > 1250*fMult)
				clr = ColorB(0,0,255,255);
			else
				clr = ColorB(nTris/10,nTris/10,nTris/10,255);

			//if (pMaterial)
			//	pMaterial = pMatMan->GetDefaultHelperMaterial();
			//if (pObj)
			pObj->m_II.m_AmbColor = ColorF(clr.r/*/155.0f*/,clr.g/*/155.0f*/,clr.b/*/155.0f*/,1);

			if (!bNoText)
				g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d",nTris );

		}
		break;
	case 3:
		{
			//////////////////////////////////////////////////////////////////////////
			// Show Lods
			//////////////////////////////////////////////////////////////////////////
			ColorB clr;
			if (numLODs < 2)
			{
				if (nTris <= 30)
				{
					clr = ColorB(50,50,50,255);
				}
				else
				{
					clr = ColorB(255,0,0,255);
					float fAngle = gEnv->pTimer->GetFrameStartTime().GetPeriodicFraction(1.0f)*gf_PI2;
					clr.g = 127 + (int)(sin(fAngle)*120);	// flashing color
				}
			}
			else
			{
				if (nLOD == 1)
					clr = ColorB(255,0,0,255);
				else if (nLOD == 2)
					clr = ColorB(0,255,0,255);
				else if (nLOD == 3)
					clr = ColorB(0,0,255,255);
				else if (nLOD == 4)
					clr = ColorB(0,255,255,255);
				else
					clr = ColorB(255,255,255,255);
			}

			//if (pMaterial)
			//	pMaterial = GetMatMan()->GetDefaultHelperMaterial();
			if (pObj)
			{
				pObj->m_II.m_AmbColor = ColorF(clr.r,clr.g,clr.b,1);
			}

			if (numLODs > 1 && !bNoText)
				g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d/%d",nLOD+1,numLODs );
		}
		break;

	case 4:
		if(pRenderMesh)
		{
			int nTexMemUsage = pRenderMesh->GetTextureMemoryUsage( pMaterial );
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d",nTexMemUsage/1024 );
		}

		break;

	case 5:
		{
			ColorB clr;
			if (nRenderMats == 1)
				clr = ColorB(0,0,255,255);
			else if (nRenderMats == 2)
				clr = ColorB(0,255,255,255);
			else if (nRenderMats == 3)
				clr = ColorB(0,255,0,255);
			else if (nRenderMats == 4)
				clr = ColorB(255,0,255,255);
			else if (nRenderMats == 5)
				clr = ColorB(255,255,0,255);
			else if (nRenderMats >= 6)
				clr = ColorB(255,0,0,255);
			else if (nRenderMats >= 11)
				clr = ColorB(255,255,255,255);

			pObj->m_II.m_AmbColor = ColorF(clr.r,clr.g,clr.b,1);

			if (!bNoText)
				g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d",nRenderMats );
		}
		break;

	case 6:
		{
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d,%d,%d,%d",
				(int)(RendParams.AmbientColor.r*255.0f),(int)(RendParams.AmbientColor.g*255.0f),(int)(RendParams.AmbientColor.b*255.0f),(int)(RendParams.AmbientColor.a*255.0f)
				);
		}
		break;

	case 7:
		if(pRenderMesh)
		{
			int nTexMemUsage = pRenderMesh->GetTextureMemoryUsage( pMaterial );
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d,%d,%d",nTris, nRenderMats,nTexMemUsage/1024 );
		}
		break;

	case 20:
		if(pRenderMesh)
		{
			const int nTexMemUsage = pRenderMesh->GetTextureMemoryUsage( pMaterial, NULL, true );
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d",nTexMemUsage/1024 );
		}
		break;

	case 21:
		if(pRenderMesh)
		{
			AABB bbox;
			pRenderMesh->GetBBox(bbox.min, bbox.max);
			bbox.SetTransformedAABB(rRenderMat34, bbox);
			float fEntDistance = cry_sqrtf(Distance::Point_AABBSq(g_pI3DEngine->GetCurrentCamera().GetPosition(), bbox)); // activate objects before they get really visible
			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%.2f",fEntDistance );
		}
		break;

	case 10:
		{
			//DrawWireframe(pInstance, nLOD, rRenderMat34);
		}
		break;

	case 19:	// Displays the triangle count of physic proxies.
		if (!bNoText)
		{
			int nPhysTrisCount = 0;
			phys_geometry *pgeom;
			if(nLOD<2) 
			{
				for(int i=m_pModel->GetNumJoints()-1; i>=0; --i)
				{
					if (pgeom = m_pModel->GetJointPhysGeom((uint32)i,nLOD))
					{
						nPhysTrisCount += pgeom->pGeom->GetPrimitiveCount();
					}
				}
			}

			if (nPhysTrisCount == 0)
				color[3] = 0.1f;

			g_pIRenderer->DrawLabelEx( trans,1.3f,color,true,true,"%d",nPhysTrisCount );
		}
		break;

	default:
		break;
	}

}




size_t CModelMesh::SizeOfModelMesh() const
{
	uint32 nSize = sizeof(CModelMesh);


	nSize += m_morphTargets.get_alloc_size();
	uint32 numMorphs = m_morphTargets.size();
	for (uint32 i=0; i<numMorphs; i++)
	{
		if (m_morphTargets[i]) 
		{
			nSize += m_morphTargets[i]->m_vertices.get_alloc_size();
			nSize += m_morphTargets[i]->m_name.capacity();
		}
	}

	nSize += m_arrExtVerticesCached.get_alloc_size();
	nSize += m_arrIntVertices.get_alloc_size();
	nSize += m_arrExtBoneIDs.get_alloc_size();
	return nSize;
}


void CModelMesh::GetMemoryUsage(ICrySizer *pSizer) const
{
	pSizer->AddObject( m_morphTargets );
	pSizer->AddObject( m_arrExtVerticesCached );
	pSizer->AddObject( m_arrIntVertices );
	pSizer->AddObject( m_arrExtBoneIDs );
}

